<template>
  <div>
    <!-- 扫描设备 -->
    <el-button @click="scanDevices">扫描设备</el-button>
    <!-- 设备表格 -->
    <el-table :data="devices" class="hid-table">
      <!-- 产品名称 -->
      <el-table-column prop="productName" label="产品名称"></el-table-column>
      <!-- 厂商ID -->
      <el-table-column prop="vendorId" label="厂商 ID">
        <template slot-scope="scope">
          {{ formatId(scope.row.vendorId) }}
        </template>
      </el-table-column>
      <!-- 产品ID -->
      <el-table-column prop="productId" label="产品 ID">
        <template slot-scope="scope">
          {{ formatId(scope.row.productId) }}
        </template>
      </el-table-column>
    </el-table>
    <!-- HID设备下拉框 -->
    <el-select v-model="selectedDevice" placeholder="选择HID设备" class="hid-select" @change="handleDeviceChange">
      <el-option v-for="device in devices" :key="device.deviceId" :label="device.productName" :value="device"/>
    </el-select>
    <!-- 连接 -->
    <el-button @click="connect" :disabled="!selectedDevice">连接</el-button>
    <!-- 断开连接 -->
    <el-button @click="disconnect" :disabled="!connectedDevice">断开连接</el-button>
    <!-- 切换为十进制 -->
    <el-button @click="toggleFormat('decimal')" :disabled="format === 'decimal'" class="hid-format-button">
      切换为十进制
    </el-button>
    <!-- 切换为十六进制 -->
    <el-button @click="toggleFormat('hex')" :disabled="format === 'hex'" class="hid-format-button">切换为十六进制
    </el-button>
    <!-- 发送数据区域 -->
    <div class="data-input">
      <el-input v-model="dataToSend" placeholder="输入要发送的数据" style="margin-bottom: 10px"></el-input>
      <el-button @click="sendData" :disabled="!connectedDevice">发送</el-button>
      <el-button @click="executeCommand(1)">设备握手</el-button>
      <el-button @click="executeCommand(2)">录制指纹</el-button>
      <el-button @click="executeCommand(3)">上报指纹</el-button>
      <el-button @click="executeCommand(4)">删除指纹</el-button>
    </div>
    <!-- 数据接收区域 -->
    <div class="received-data" ref="receivedDataContainer" style="height: 400px;margin-bottom: 10px">
      <p>接收到的数据：</p>
      <div v-for="data in receivedDataList" :key="data.id" class="received-data-item">
        <span class="received-data-time">{{ data.time }}</span>
        <pre>{{ data.content }}</pre>
      </div>
    </div>
    <!-- 清空数据 -->
    <el-button @click="clearReceivedData">清空数据</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      devices: [],             // 存储扫描到的HID设备
      selectedDevice: null,    // 用户选择的HID设备
      connectedDevice: null,   // 当前连接的HID设备
      format: 'decimal',       // 设备 ID 和厂商 ID 展示的格式（'decimal' 或 'hex'）
      dataToSend: '',          // 要发送的数据
      receivedDataList: []     // 接收到的数据列表
    }
  },
  // 页面创建时
  created() {
    // 在页面创建时添加 `beforeunload` 事件监听器
    window.addEventListener('beforeunload', this.handlePageUnload)
  },
  // 导航守卫中断开设备连接
  beforeRouteLeave(to, from, next) {
    console.log('导航守卫中断开设备连接')
    // 断开连接的操作
    this.disconnect()
    // 重置设备对象
    this.handlePageUnload()
    next()
  },
  methods: {
    // 在页面刷新时断开连接并重置设备对象
    handlePageUnload() {
      this.disconnect()
      // 重置设备相关状态
      this.selectedDevice = null
      this.connectedDevice = null
    },
    // 扫描HID设备
    async scanDevices() {
      try {
        // 获取所有HID设备
        const devices = await navigator.hid.requestDevice({ filters: [] })
        console.log('扫描到' + devices.length + '个USB设备')
        this.devices = devices
      } catch (error) {
        console.error('Failed to scan HID devices:', error)
      }
    },
    // 切换格式
    toggleFormat(format) {
      this.format = format
    },
    // 处理设备更改
    handleDeviceChange() {
      this.connectedDevice = null
    },
    // 连接
    async connect() {
      if (this.selectedDevice) {
        const device = this.selectedDevice
        if (device) {
          try {
            await device.open()
            device.addEventListener('inputreport', this.handleInputReport)
            this.connectedDevice = device
          } catch (error) {
            this.$message.error('连接HID设备失败: ' + error.message)
            console.error('Failed to connect to HID device:', error)
          }
        }
      }
    },
    // 断开连接
    async disconnect() {
      if (this.connectedDevice) {
        try {
          // 移除处理输入报告的监听事件
          this.connectedDevice.removeEventListener('inputreport', this.handleInputReport)
          // 断开设备连接
          await this.connectedDevice.close()
          // 重置连接对象
          this.connectedDevice = null
        } catch (error) {
          // 错误提示
          console.error('Failed to disconnect from HID device:', error)
        }
      }
    },
    // 格式化ID
    formatId(id) {
      // 根据格式选择展示方式
      return this.format === 'hex' ? '0x' + Number(id).toString(16).toUpperCase() : String(id)
    },
    // 发送数据
    async sendData() {
      if (this.connectedDevice && this.dataToSend) {
        // 转为字节数组
        const byteArray = this.hexStringToByteArray(this.dataToSend.trim())
        const data = new Uint8Array(byteArray)
        try {
          // data必须是Uint8Array类型的数据。这是因为 HID 设备通常接收和发送的数据都是以字节数组的形式进行传输的。
          await this.connectedDevice.sendReport(0, data)
        } catch (error) {
          this.$message.error('发送数据失败: ' + error.message)
          console.error('Failed to send data:', error)
        }
      }
    },
    // 执行命令
    executeCommand: async function(cmd) {
      let dataStr = null
      // 自定义指令
      if (cmd === 1) {
        dataStr = '11D20006A00178'
      } else if (cmd === 2) {
        dataStr = '11D20006A2017A'
      } else if (cmd === 3) {
        dataStr = '11D20009A3E4303102C3'
      } else if (cmd === 4) {
        dataStr = '11D20009A4E4303002C3'
      }
      const byteArray = this.hexStringToByteArray(dataStr.trim())
      const data = new Uint8Array(byteArray)
      try {
        //  data必须是Uint8Array 类型的数据。这是因为 HID 设备通常接收和发送的数据都是以字节数组的形式进行传输的。
        await this.connectedDevice.sendReport(0, data)
      } catch (error) {
        this.$message.error('发送数据失败: ' + error.message)
        console.error('Failed to send data:', error)
      }
    },
    // 十六进制字符串到字节数组
    hexStringToByteArray(hexString) {
      // 定义byte数组存储数据
      const byteArray = []
      for (let i = 0; i < hexString.length; i += 2) {
        // 将字符串以2位分割转为16进制的数据添加到byte数组
        byteArray.push(parseInt(hexString.substr(i, 2), 16))
      }
      return byteArray
    },
    // 处理输入报告
    handleInputReport(event) {
      // 拿到接收的数据
      const receivedData = event.data
      // 获取当前的时间
      const time = new Date().toLocaleString('zh-CN')
      // 格式化接收数据
      const content = this.formatReceivedData(receivedData)
      // 追加数据到接收数据列表
      this.receivedDataList.push({ id: Date.now(), time, content })
      // 滚动到底部
      this.$nextTick(() => {
        // 获取接收数据区域的引用
        const container = this.$refs.receivedDataContainer
        // container.scrollTop 是滚动容器的滚动位置，将其设置为 container.scrollHeight，即滚动到底部。
        container.scrollTop = container.scrollHeight
      })
    },
    // 格式化接收到的数据
    formatReceivedData(data) {
      if (data instanceof ArrayBuffer) {
        const byteArray = new Uint8Array(data)
        let hexString = ''
        // 循环处理数据
        for (let i = 1; i < byteArray.length; i++) {
          const byte = byteArray[i]
          hexString += byte.toString(16).padStart(2, '0') + ' '
        }
        // 数据转为大写
        return hexString.trim().toUpperCase()
      } else if (data instanceof DataView) {
        const byteLength = data.byteLength
        let hexString = ''
        // 循环处理数据
        for (let i = 1; i < byteLength; i++) {
          const byte = data.getUint8(i)
          hexString += byte.toString(16).padStart(2, '0') + ' '
        }
        // 数据转为大写
        return hexString.trim().toUpperCase()
      } else if (typeof data === 'string') {
        // 如果数据已经是字符串类型，则直接返回
        return data
      }

      return ''
    },
    // 清除接收的数据
    clearReceivedData() {
      this.receivedDataList = []
    }
  }
}
</script>

<style scoped>
.hid-table {
  margin-top: 20px;
}

.hid-select {
  margin-top: 20px;
}

.hid-format-button {
  margin-top: 20px;
}

.data-input {
  margin-top: 20px;
}

.received-data {
  margin-top: 10px;
  border: 1px solid #ccc;
  padding: 10px;
  background-color: #f5f5f5;
  height: 200px;
  overflow-y: auto;
}

.received-data-item {
  margin-bottom: 10px;
}

.received-data-time {
  font-weight: bold;
  margin-bottom: 5px;
}
</style>
